/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.manager;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.openislands.oi.config.MyNodeInfo;
import org.openislands.oi.constant.JobState;
import org.openislands.oi.constant.MessageCodeEnum;
import org.openislands.oi.constant.TaskState;
import org.openislands.oi.dao.entity.JobDO;
import org.openislands.oi.dao.entity.TaskDO;
import org.openislands.oi.dao.mapper.JobMapper;
import org.openislands.oi.dao.mapper.TaskMapper;
import org.openislands.oi.error.LogicException;
import org.openislands.oi.pojo.base.BeanBuilder;
import org.openislands.oi.pojo.dto.JobDTO;
import org.openislands.oi.pojo.dto.TaskDTO;
import org.openislands.oi.scheduling.StateEngine;
import org.openislands.oi.util.ValueCopyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class CommonJobManager {
    private static final Logger log = LoggerFactory.getLogger(CommonJobManager.class);

    private final GeneralManager<JobMapper, JobDO> jobManager;

    private final GeneralManager<TaskMapper, TaskDO> taskManager;

    @Resource
    private CommonOperatorManager commonOperatorManager;

    @Resource
    private CommonOfNodeTaskManager commonOfNodeTaskManager;

    @Resource
    private StateEngine stateEngine;

    @Resource
    private MyNodeInfo myNodeInfo;

    public CommonJobManager(JobMapper jobMapper, TaskMapper taskMapper) {
        this.jobManager = new GeneralManager<>(jobMapper);
        this.taskManager = new GeneralManager<>(taskMapper);
    }

    public GeneralManager<JobMapper, JobDO> getJobManager() {
        return jobManager;
    }

    public GeneralManager<TaskMapper, TaskDO> getTaskManager() {
        return taskManager;
    }

    public JobDTO queryJob(String jobId) {
        try {
            return jobManager.queryOneDTO(BeanBuilder.create(new JobDTO()).set(JobDTO::setJobId, jobId).build());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new LogicException(MessageCodeEnum.DATA_REDUNDANCY_ERROR, "find more than one");
        }
    }

    public List<JobDTO> queryJobByState(JobState jobState, String... leaderNode) {
        JobDTO jobDTO = new JobDTO();
        jobDTO.setState(jobState);
        jobDTO.setLeaderNode(leaderNode != null && leaderNode.length > 0 ? leaderNode[0] : null);
        return this.queryAnyJob(jobDTO);
    }

    public List<JobDTO> queryAnyJob(JobDTO jobDTO) {
        return jobManager.queryAnyLikeDTO(jobDTO);
    }

    public Boolean updateJob(String jobId, JobDTO jobDTO) {
        jobDTO.setJobId(jobId);
        return this.updateAnyJob(jobDTO, JobDO::getJobId);
    }

    @SafeVarargs
    public final <Res> Boolean updateAnyJob(JobDTO jobDTO, SFunction<JobDO, Res>... functions) {
        return jobManager.updateAnyLikeTask(jobDTO, JobDO::getState, jobDO -> jobDO.setState(null), functions);
    }

    public MessageCodeEnum updateJobState(String jobId, JobState toState) {
        return stateEngine.updateState(jobManager.getBaseMapper(), toState
                , JobDO::getState
                , JobState::name
                , lambdaWrapper -> lambdaWrapper.eq(JobDO::getJobId, jobId)
        ).getCode();
    }

    public Boolean deleteJob(String jobId) {
        Boolean res = this.deleteTaskByJob(jobId);
        if (res) {
            return jobManager.delete(uw -> uw.eq(JobDO::getJobId, jobId));
        } else {
            throw new LogicException(MessageCodeEnum.DATA_UPDATE_ERROR, "delete all tasks of the job failed");
        }
    }

    public Boolean deleteTaskByJob(String jobId) {
        boolean rt = taskManager.delete(uw -> uw.eq(TaskDO::getJobId, jobId));
        boolean st = commonOfNodeTaskManager.deleteOfNodeTaskByJob(jobId);
        return rt && st;
    }

    public Boolean deleteTask(String jobId, String taskName, Integer taskVersion) {
        boolean st = commonOfNodeTaskManager.deleteOfNodeTask(jobId, taskName, taskVersion);
        return st & taskManager.delete(uw -> uw.eq(TaskDO::getJobId, jobId).eq(TaskDO::getTaskName, taskName).eq(TaskDO::getTaskVersion, taskVersion));
    }

    public TaskDTO queryTask(String jobId, String taskName, Integer taskVersion) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setJobId(jobId);
        taskDTO.setTaskName(taskName);
        taskDTO.setTaskVersion(taskVersion);
        List<TaskDTO> taskDTOList = this.queryAnyTask(taskDTO);
        if (taskDTOList.isEmpty()) {
            return null;
        } else if (taskDTOList.size() > 1) {
            throw new LogicException(MessageCodeEnum.DATA_REDUNDANCY_ERROR, "find more than one");
        }
        return taskDTOList.get(0);
    }

    public List<TaskDTO> queryTaskByJob(String jobId) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setJobId(jobId);
        return this.queryAnyTask(taskDTO);
    }

    public List<TaskDTO> queryTaskByState(TaskState taskState) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setState(taskState);
        return this.queryAnyTask(taskDTO);
    }

    public List<TaskDTO> queryAnyTask(TaskDTO taskDTO) {
        return taskManager.queryAnyLikeDTO(taskDTO);
    }

    public Boolean updateTask(String jobId, String taskName, Integer taskVersion, TaskDTO taskDTO) {
        taskDTO.setJobId(jobId);
        taskDTO.setTaskName(taskName);
        taskDTO.setTaskVersion(taskVersion);
        return this.updateAnyTask(taskDTO, TaskDO::getJobId, TaskDO::getTaskName, TaskDO::getTaskVersion);
    }

    public MessageCodeEnum updateTaskState(String jobId, String taskName, Integer taskVersion, TaskState toState) {
        return stateEngine.updateState(taskManager.getBaseMapper(), toState
                , TaskDO::getState
                , TaskState::name
                , lambdaWrapper -> lambdaWrapper.eq(TaskDO::getJobId, jobId).eq(TaskDO::getTaskName, taskName).eq(TaskDO::getTaskVersion, taskVersion)
        ).getCode();
    }

    @SafeVarargs
    public final <Res> Boolean updateAnyTask(TaskDTO taskDTO, SFunction<TaskDO, Res>... functions) {
        return taskManager.updateAnyLikeTask(taskDTO, TaskDO::getState, taskDO -> taskDO.setState(null), functions);
    }

    public List<TaskDTO> queryTaskOrderByVersion(TaskDTO taskDTO) {
        List<TaskDO> taskDOList = taskManager.queryAnyList(uw ->
                uw.setEntity(taskManager.copy(taskDTO)).orderByDesc(TaskDO::getTaskVersion)
        );
        return ValueCopyUtils.copyBuilderList(taskDOList, builder -> builder.build(TaskDTO.class));
    }
}
